import re

from wheels.pi.lang.Object import Object


class Uri(Object):
    """
    A uniform resource identifier.

    See also:
    RFC 3986

    Todo:
    * URL encoding/decoding (see urllib).
    """

    __REGEX = "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?"


    @staticmethod
    def fromString(s):
	"""
	Construct a URI from the specified string.

	Keyword arguments:
	s - (str) A string.
	"""
	match = re.match(Uri.__REGEX, s)
	scheme = match.group(2)
	authority = match.group(4)
	path = match.group(5)
	query = match.group(7)
	fragment = match.group(9)
	return Uri(scheme, authority, path, query, fragment)

    @staticmethod
    def __mergePaths(uri1, uri2):
	base = uri1
	r = uri2
	if base.__authority and not base.__path:
	    return "/" + r.__path
	return re.match("^.*/", base.__path).group(0) + r.__path

    @staticmethod
    def __removeDotSegments(uri):
	ibuf = uri.__path
	obuf = ""
	while ibuf != "":
	    if ibuf[:3] == "../":
		ibuf = ibuf[3:]
	    elif ibuf[:2] == "./":
		ibuf = ibuf[2:]
	    elif ibuf[:3] == "/./":
		ibuf = "/" + ibuf[3:]
	    elif ibuf == "/.":
		ibuf = "/"
	    elif ibuf[:4] == "/../":
		ibuf = "/" + ibuf[4:]
		obuf = re.sub("/?[^/]*$", "", obuf)
	    elif ibuf == "/..":
		ibuf = "/"
		obuf = re.sub("/?[^/]*$", "", obuf)
	    elif ibuf in (".", ".."):
		ibuf = ""
	    else:
		match = re.match("^/?[^/]*", ibuf)
		ibuf = ibuf[len(match.group(0)):]
		obuf += match.group(0)
	return obuf


    def __init__(self, scheme = "", authority = "", path = "",
		 query = "", fragment = ""):
	"""
	Initialize this URI.

	Keyword arguments:
	scheme -- (str) The scheme component.
	authoriy -- (str) The authority component.
	path -- (str) The path component.
	query -- (str) The query component.
	fragment -- (str) The fragment component.
	"""
	self.__scheme = scheme
	self.__authority = authority
	self.__path = path
	self.__query = query
	self.__fragment = fragment

    def getScheme(self):
	"""
	Return the scheme component of this URI.

	Returns:
	(str) The scheme string.
	"""
	return self.__scheme;

    def getAuthority(self):
	"""
	Return the authority component of this URI.

	Returns:
	(str) The authority component.
	"""
	return self.__authority;

    def getPath(self):
	"""
	Return the path component of this URI.

	Returns:
	(str) The path component.
	"""
	return self.__path;

    def getQuery(self):
	"""
	Return the query component of this URI.

	Returns:
	(str) The query component.
	"""
	return self.__query;

    def getFragment(self):
	"""
	Return the fragment component of this URI.

	Returns:
	(str) The fragment component.
	"""
	return self.__fragment;

    def recompose(self):
	"""
	Return the string representation of this URI.

	Returns:
	(str) The string that represents this URI.
	"""
	s = ""
	if self.__scheme:
	    s += self.__scheme + ":"
	if self.__authority:
	    s += "//" + self.__authority
	s += self.__path
	if self.__query:
	    s += "?" + self.__query
	if self.__fragment:
	    s += "#" + self.__fragment
	return s

    def resolve(self, uri):
	"""
	Resolve the specified URI with respect to this URI.

	Keyword arguments:
	uri -- (Uri) A URI.

	Returns:
	(Uri) The resolved URI.
	"""
	if not isinstance(uri, Uri):
	    uri = Uri.fromString(uri)
	r = uri
	t = Uri()
	base = self
	if r.__scheme:
	    t.__scheme = r.__scheme
	    t.__authority = r.__authority
	    t.__path = Uri.__removeDotSegments(r)
	    t.__query = r.__query
	else:
	    if r.__authority:
		t.__authority = r.__authority
		t.__path = Uri.__removeDotSegments(r)
		t.__query = r.__query
	    else:
		if not r.__path:
		    t.__path = base.__path
		    if r.__query:
			t.__query = r.__query
		    else:
			t.__query = base.__query
		else:
		    if r.__path[0] == "/":
			t.__path = Uri.__removeDotSegments(r)
		    else:
			t.__path = Uri.__mergePaths(base, r)
			t.__path = Uri.__removeDotSegments(t)
		    t.__query = r.__query
		t.__authority = base.__authority
	    t.__scheme = base.__scheme
	t.__fragment = r.__fragment
	return t

    def isAbsolute(self):
	"""
	Test if this URI is absolute.

	Returns:
	(bool) True if this URI is absolute; false otherwise.
	"""
	return bool(self.__scheme)

    def __eq__(self, other):
        if not isinstance(other, Uri):
            return False
        if self.__scheme != other.__scheme:
            return False
        if self.__authority != other.__authority:
            return False
        if self.__path != other.__path:
            return False
        if self.__query != other.__query:
            return False
        if self.__fragment != other.__fragment:
            return False
        return True

    def __str__(self):
	"""
	Return the string representation of this URI.

	Returns:
	(str) The string.
	"""
	return self.recompose()




#
# Reference resolution examples (RFC 3986, Section 5.4)
#
# Type 'python -m "wheels.pi.net.Uri"' on the command line.
#
if __name__ == "__main__":
    def test(base, r, t0):
        print "\"" + str(r) + "\"", "-->", "\"" + str(t0) + "\""
        t = base.resolve(r)
        ok = t == t0
        if not ok:
            print "  INCORRECT:", str(t)
    normalExamples = [("g:h", "g:h"),
                      ("g", "http://a/b/c/g"),
                      ("./g", "http://a/b/c/g"),
                      ("g/", "http://a/b/c/g/"),
                      ("/g", "http://a/g"),
                      ("//g", "http://g"),
                      ("?y", "http://a/b/c/d;p?y"),
                      ("g?y", "http://a/b/c/g?y"),
                      ("#s", "http://a/b/c/d;p?q#s"),
                      ("g#s", "http://a/b/c/g#s"),
                      ("g?y#s", "http://a/b/c/g?y#s"),
                      (";x", "http://a/b/c/;x"),
                      ("g;x", "http://a/b/c/g;x"),
                      ("g;x?y#s", "http://a/b/c/g;x?y#s"),
                      ("", "http://a/b/c/d;p?q"),
                      (".", "http://a/b/c/"),
                      ("./", "http://a/b/c/"),
                      ("..", "http://a/b/"),
                      ("../", "http://a/b/"),
                      ("../g", "http://a/b/g"),
                      ("../..", "http://a/"),
                      ("../../", "http://a/"),
                      ("../../g", "http://a/g")]
    abnormalExamples = [("../../../g", "http://a/g"),
                        ("../../../../g", "http://a/g"),
                        ("/./g", "http://a/g"),
                        ("/../g", "http://a/g"),
                        ("g.", "http://a/b/c/g."),
                        (".g", "http://a/b/c/.g"),
                        ("g..", "http://a/b/c/g.."),
                        ("..g", "http://a/b/c/..g"),
                        ("./../g", "http://a/b/g"),
                        ("./g/.", "http://a/b/c/g/"),
                        ("g/./h", "http://a/b/c/g/h"),
                        ("g/../h", "http://a/b/c/h"),
                        ("g;x=1/./y", "http://a/b/c/g;x=1/y"),
                        ("g;x=1/../y", "http://a/b/c/y"),
                        ("g?y/./x", "http://a/b/c/g?y/./x"),
                        ("g?y/../x", "http://a/b/c/g?y/../x"),
                        ("g#s/./x", "http://a/b/c/g#s/./x"),
                        ("g#s/../x", "http://a/b/c/g#s/../x"),
                        ("http:g", "http:g")]
    base = Uri.fromString("http://a/b/c/d;p?q")
    print "BASE URI:", "\"" + str(base) + "\""
    print "NORMAL EXAMPLES:"
    for (candidate, correct) in normalExamples:
        r = Uri.fromString(candidate)
        t0 = Uri.fromString(correct)
        test(base, r, t0)
    print "ABNORMAL EXAMPLES:"
    for (candidate, correct) in abnormalExamples:
        r = Uri.fromString(candidate)
        t0 = Uri.fromString(correct)
        test(base, r, t0)
